package com.apobates.jforum2.troll.replies.biz.impl;

import com.apobates.jforum2.troll.attention.core.decorater.ForumEncoder;
import com.apobates.jforum2.troll.regular.ActionDescriptor;
import com.apobates.jforum2.troll.regular.ForumActionEnum;
import com.apobates.jforum2.troll.regular.ImageIOMeta;
import com.apobates.jforum2.troll.replies.biz.PostsService;
import com.apobates.jforum2.troll.replies.biz.dao.PostsDao;
import com.apobates.jforum2.troll.replies.biz.impl.api.PostsActionLogStreamProvider;
import com.apobates.jforum2.troll.replies.biz.impl.api.PostsFeignServiceApi;
import com.apobates.jforum2.troll.replies.decorate.ForumPostsDecorator;
import com.apobates.jforum2.troll.replies.entity.Posts;
import com.apobates.jforum2.troll.utils.core.DateTimeUtils;
import com.apobates.jforum2.troll.utils.core.api.ActionEventCulpritor;
import com.apobates.jforum2.troll.utils.persistence.Page;
import com.apobates.jforum2.troll.utils.persistence.Pageable;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import io.seata.spring.annotation.GlobalTransactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
 *
 * @author xiaofanku
 * @since 20200512
 */
@Service
public class PostsServiceImpl implements PostsService{
    @Autowired
    private PostsDao postsDao;
    @Autowired
    private PostsFeignServiceApi postsFeignServiceApi;
    @Autowired
    private PostsActionLogStreamProvider postsActionLogStreamProvider;
    private final static Logger logger = LoggerFactory.getLogger(PostsServiceImpl.class);

    @ActionDescriptor(action=ForumActionEnum.POSTS_REPLY, isBool=false)
    @Transactional
    @GlobalTransactional(rollbackFor = Exception.class)
    @Override
    public Posts create(int boardGroupId, long boardId, long topicId, String content, ImageIOMeta imageIO, ActionEventCulpritor culpritor) throws IllegalStateException, IllegalArgumentException { //ASP-P1
        logger.error("[replies-api][SRV] create posts start");
        //编码回复内容
        String encodePostsContent = new ForumEncoder(content)
                .encodeUploadImage(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName())
                .relaxedHtmlTag()
                .parseEmoji()
                .getContent();
        //-------------------------------------------------------------------------
        //楼层交由事务去填充
        try{
            logger.error("[replies-api][SRV] posts instruct");
            Posts posts = new Posts(encodePostsContent, culpritor.getMemberId(), culpritor.getMemberNickname(), topicId, boardGroupId, boardId, -1, culpritor.getIpAddr());
            postsDao.save(posts);
            logger.error("[replies-api][SRV] posts persiste end");
            if (posts.getId() > 0) {
                logger.error("[replies-api][SRV] posts persiste success");
                //postsActionLogStreamProvider.entryTopicContent(posts, culpritor);
                //new PostsPublishEvent(posts, culpritor.getUserAgent()).sendBy(jmsTemplate);
                //PostsPublishEvent
                //内部：{更新版块和话题的统计}
                //外部：{话题开启了回复通知负责向楼主发送通知,回复的引用通知}

                //更新话题统计最近的回复
                logger.error("[replies-api][SRV]update topicstats recent reply");
                postsFeignServiceApi.updateStatsRecentReply(posts.getTopicId(), posts.getId(), posts.getMemberId(), posts.getMemberNickname());
                //更新版块统计的回复数量
                logger.error("[replies-api][SRV]update boardstats reply size");
                postsFeignServiceApi.updateStatsReplies(posts.getBoardId(), posts.getTopicId(), posts.getId());
                return posts;
            }
        }catch(Exception e){
            logger.error("[replies-api][SRV] posts persiste fail, has exception:"+e.getMessage());
            if(logger.isDebugEnabled()){
                logger.debug("话题回复创建失败", e);
            }
            
        }
        throw new IllegalStateException("话题回复创建失败");
    }

    //1:从创建话题中分离出来的事务
    @Transactional
    @Override
    public Posts entry(int boardGroupId, long boardId, long topicId, String content, ImageIOMeta imageIO, ActionEventCulpritor culpritor) throws IllegalStateException, IllegalArgumentException {
        // 话题内容
        // 从话题创建中脱离而出的事务
        final ForumEncoder postsEncode = new ForumEncoder(content).encodeUploadImage(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName());
        logger.error("[replies-TC]entry start");
        try{
            Posts oneFloorPosts = new Posts(postsEncode.relaxedHtmlTag().parseEmoji().getContent(), culpritor.getMemberId(), culpritor.getMemberNickname(), topicId, boardGroupId, boardId, culpritor.getIpAddr(), false);
            postsDao.save(oneFloorPosts);
            if (oneFloorPosts.getId() > 0) {
                logger.error("[replies-TC]entry topic content:"+oneFloorPosts.getId()+"-"+topicId);
                //new PostsPublishEvent(posts, culpritor.getUserAgent()).sendBy(jmsTemplate);
                postsActionLogStreamProvider.entryTopicContent(oneFloorPosts, culpritor);
                return oneFloorPosts;
            }else{
                logger.error("[replies-TC]entry topic content error");
            }
        }catch(Exception e){
            logger.error("[replies-TC]entry fail reason:"+e.getMessage());
            if(logger.isDebugEnabled()){
                logger.debug("话题内容写入失败", e);
            }
        }
        logger.error("[replies-TC]entry content exception IllegalStateException");
        throw new IllegalStateException("话题内容写入失败");
    }
    
    @ActionDescriptor(action=ForumActionEnum.POSTS_EDIT)
    @Transactional
    @GlobalTransactional
    @Override
    public Optional<Boolean> edit(long id, String content, ImageIOMeta imageIO, ActionEventCulpritor culpritor)throws IllegalStateException { //ASP-P2
        //编码回复内容
        String encodePostsContent = new ForumEncoder(content)
                .encodeUploadImage(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName())
                .relaxedHtmlTag()
                .parseEmoji()
                .getContent();
        //-------------------------------------------------------------------------
        try{
            //加工内容
            return postsDao.edit(id, encodePostsContent, culpritor.getMemberId(), culpritor.getMemberNickname());
        }catch(Exception e){
            if(logger.isDebugEnabled()){
                logger.debug("回复编辑失败", e);
            }
            throw new IllegalStateException("回复编辑失败");
        }
    }

    //3:从创建话题编辑中分离出来的事务
    @Transactional
    @Override
    public Optional<Boolean> modifyTopic(long topicId, String content, long modifyMember, String modifyMemberNickname, ImageIOMeta imageIO) throws IllegalStateException {
        final ForumEncoder postsEncode = new ForumEncoder(content).encodeUploadImage(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName());
        //编辑话题内容
        String modifyTopicContent = postsEncode.parseEmoji().getContent(); //ST2
        //-------------------------------------------------------------------------
        try{
            //加工内容
            postsDao.editTopicContent(topicId, modifyTopicContent, modifyMember, modifyMemberNickname);
            return Optional.of(true);
        }catch(Exception e){
            if(logger.isDebugEnabled()){
                logger.debug("回复编辑失败", e);
            }
            throw new IllegalStateException("话题内容编辑失败");
        }
    }

    @Override
    public Optional<Posts> getPostsContentForQuote(long id, ImageIOMeta imageIO, boolean lazyLoad, String imageScale)throws IllegalArgumentException {
        if (id <= 0) {
            return Optional.empty();//("参数不合法或不被接受");
        }
        Posts posts = postsDao.findOne(id).orElseThrow(()->new IllegalArgumentException("话题回复不存在"));
        String postsBody = ForumPostsDecorator.init(posts)
                            .block("作者被禁止发言或内容自动屏蔽")
                            .decorateUploadImageUseScale(lazyLoad, imageScale)
                            .apply(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName())
                            .modify("回复最近由管理人员进行过编辑")
                            .getContent();
        posts.setContent(postsBody);
        return Optional.of(posts);
    }
    
    @Override
    public Optional<Posts> getPostsContentForEdit(long id, ImageIOMeta imageIO)throws IllegalArgumentException {
        if (id <= 0) {
            return Optional.empty();//("参数不合法或不被接受");
        }
        Posts posts = postsDao.findOne(id).orElseThrow(()->new IllegalArgumentException("话题回复不存在"));
        String postsBody = ForumPostsDecorator.init(posts)
                            .decorateUploadImageEmptyScale(false)
                            .apply(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName())
                            .getContent();
        posts.setContent(postsBody);
        return Optional.of(posts);
    }
    
    @Override
    public Stream<Posts> getRecentForRSS(long topicId, int size, ImageIOMeta imageIO, boolean lazyLoad) {
        Consumer<Posts> action = posts->{
            //解码图片连接编码
            String postsBody = ForumPostsDecorator.init(posts)
                                .block("作者被禁止发言或内容自动屏蔽")
                                .decorateUploadImageEmptyScale(lazyLoad)
                                .apply(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName())
                                .modify("回复最近由管理人员进行过编辑")
                                .getContent();
            posts.setContent(postsBody);
        };
        return postsDao.findAllByTopic(topicId, size).peek(action);
    }
    
    @ActionDescriptor(action=ForumActionEnum.POSTS_DEL)
    @Transactional
    @GlobalTransactional
    @Override
    public Optional<Boolean> remove(long id, ActionEventCulpritor culpritor) { //ASP-P3
        return postsDao.editStatus(id, false);
    }
    
    @Override
    public Page<Posts> getAllReply(long topicId, Pageable pageable) {
        return postsDao.findAllReplyByTopic(topicId, pageable);
    }
    
    @Override
    public Stream<Posts> getTopicLordContent(List<Long> topicIdList) {
        if (null == topicIdList || topicIdList.isEmpty()) {
            return Stream.empty();
        }
        return postsDao.findOneByOneFloor(topicIdList);
    }
    
    @Override
    public Stream<Posts> getRecentByUnixStamp(long topicId, int prevUnixStamp) {
        LocalDateTime prevDate = DateTimeUtils.getDateTimeByUnixTimestamp(prevUnixStamp);
        return postsDao.findAllRecentByTopic(topicId, prevDate);
    }
    
    @Override
    public Stream<Posts> getRecentForTopicIgnoreStatus(long topicId, int size) {
        return postsDao.findAllRecentByTopicIgnoreStatus(topicId, size);
    }
    
    @Override
    public Optional<Posts> get(long id) {
        if (id <= 0) {
            return Optional.empty(); //("参数不合法或不被接受");
        }
        return postsDao.findOne(id);
    }
    
    @Override
    public long maxFloor(long topicId) {
        return postsDao.maxFloor(topicId);
    }
    
    @Override
    public long getTopicPageSize(long topicId, int pageSize) {
        long mf = maxFloor(topicId);
        if (mf <= pageSize) {
            return 1;
        }
        long page = mf / pageSize;
        if (mf % pageSize != 0) {
            page += 1;
        }
        return page;
    }
    
    @Override
    public long countRepliesSize(long topicId, long replyer) {
        return postsDao.countRepliesSize(topicId, replyer);
    }
    
    @Override
    public long countRepliesSize(long topicId) {
        return postsDao.countTopicReply(topicId);
    }

    //0:从话题移动中分离出来的事务
    @Transactional
    @Override
    public int toggleReplyTopic(long topicId, long targetBoardId, int targetBoardGroupId) throws IllegalStateException {
        return postsDao.toggleReplyTopic(topicId, targetBoardId, targetBoardGroupId);
        //测试分布事务示例
        //throw new IllegalStateException("我出错了");
    }

    @Override
    public Page<Posts> getAllReply(long topicId, Pageable pageable, ImageIOMeta imageIO, boolean lazyLoad, String imageScale) {
        Page<Posts> rs = postsDao.findAllReplyByTopic(topicId, pageable);
        Consumer<Posts> pc = (Posts pr)->{
            //解码图片连接编码
            String postsBody = ForumPostsDecorator.init(pr)
                                .block("*")
                                .decorateUploadImageUseScale(lazyLoad, imageScale)
                                .apply(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName())
                                .getContent();
            pr.setContent(postsBody);
        };
        return copyPosts(rs, pc);
    }

    @Override
    public Page<Posts> getAllReplyFilterAuthor(long topicId, long filterMemeberId, Pageable pageable, ImageIOMeta imageIO, boolean lazyLoad, String imageScale) {
        if (filterMemeberId < 1) {
            return getAllReply(topicId, pageable, imageIO, lazyLoad, imageScale);
        }
        Page<Posts> rs = postsDao.findAllReplyByTopic(topicId, filterMemeberId, pageable);
        Consumer<Posts> pc = (Posts pr)->{
            //解码图片连接编码
            String postsBody = ForumPostsDecorator.init(pr)
                                .block("*")
                                .decorateUploadImageUseScale(lazyLoad, imageScale)
                                .apply(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName())
                                .getContent();
            pr.setContent(postsBody);
        };
        return copyPosts(rs, pc);
    }

    @Override
    public Page<Posts> getAllSortReplyFilterAuthor(long topicId, long filterMemeberId, int sortCategory, Pageable pageable, ImageIOMeta imageIO, boolean lazyLoad, String imageScale) {
        if(1 != sortCategory){
            //都是升序
            return getAllReplyFilterAuthor(topicId, filterMemeberId, pageable, imageIO, lazyLoad, imageScale);
        };
        //降序
        Page<Posts> rs = postsDao.findAllDescSortReplyByTopic(topicId, filterMemeberId, pageable);
        Consumer<Posts> pc = (Posts pr)->{
            //解码图片连接编码
            String postsBody = ForumPostsDecorator.init(pr)
                    .block("*")
                    .decorateUploadImageUseScale(lazyLoad, imageScale)
                    .apply(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName())
                    .getContent();
            pr.setContent(postsBody);
        };
        return copyPosts(rs, pc);
    }
    
    @Override
    public Optional<Posts> getTopicContent(long topicId) {
        if (topicId <= 0) {
            return Optional.empty();//("参数不合法或不被接受");
        }
        return postsDao.findOneByTopicId(topicId);
    }

    @Override
    public Optional<Posts> getTopicContent(long topicId, ImageIOMeta imageIO, boolean lazyload, String imageScale){
        if (topicId <= 0) {
            return Optional.empty();//("参数不合法或不被接受");
        }
        Optional<Posts> posts = postsDao.findOneByTopicId(topicId);
        if(!posts.isPresent()){
            return Optional.empty();
        }
        try {
            Posts p = posts.get();
            String oneFloorBody = ForumPostsDecorator.init(p)
                                   .block("内容自动屏蔽")
                                   .decorateUploadImageUseScale(lazyload, imageScale)
                                   .apply(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName())
                                   .getContent();
            p.setContent(oneFloorBody);
            return Optional.of(p);
         }catch (NullPointerException e){
            return Optional.empty();
         }
    }

    private CompletableFuture<Posts> getAsync(final long id){
        return CompletableFuture.supplyAsync(()->postsDao.findOneById(id));
    }
    /*
    @Override
    public Page<PostsReplica> getAll(long topicId, Pageable pageable) {
        Page<Posts> rs = postsDao.findAllByTopic(topicId, pageable);
        return copyPosts(rs, null);
    }
    //20200529
    @Override
    public Page<PostsReplica> getAllReply(long topicId, Pageable pageable, ImageIOMeta imageIO, boolean lazyLoad, String imageScale) {
        Page<Posts> rs = postsDao.findAllReplyByTopic(topicId, pageable);
        Consumer<PostsReplica> pc = (PostsReplica pr)->{
            //解码图片连接编码
            String postsBody = ForumPostsDecorator.init(pr)
                                .block("*")
                                .decorateUploadImageUseScale(lazyLoad, imageScale)
                                .apply(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName())
                                .getContent();
            pr.setContent(postsBody);
        };
        return copyPosts(rs, pc);
    }*/
    /*20200529
    @Override
    public Page<PostsReplica> getAllReplyFilterAuthor(long topicId, long filterMemeberId, Pageable pageable, ImageIOMeta imageIO, boolean lazyLoad, String imageScale) {
        if (filterMemeberId < 1) {
            return getAllReply(topicId, pageable, imageIO, lazyLoad, imageScale);
        }
        Page<Posts> rs = postsDao.findAllReplyByTopic(topicId, filterMemeberId, pageable);
        Consumer<PostsReplica> pc = (PostsReplica pr)->{
            //解码图片连接编码
            String postsBody = ForumPostsDecorator.init(pr)
                                .block("*")
                                .decorateUploadImageUseScale(lazyLoad, imageScale)
                                .apply(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName())
                                .getContent();
            pr.setContent(postsBody);
        };
        return copyPosts(rs, pc);
    }*/
/*
    @Override
    public Page<PostsReplica> getAllSortReplyFilterAuthor(long topicId, long filterMemeberId, int sortCategory, Pageable pageable, ImageIOMeta imageIO, boolean lazyLoad, String imageScale) {
        if(1 != sortCategory){
            //都是升序
            return getAllReplyFilterAuthor(topicId, filterMemeberId, pageable, imageIO, lazyLoad, imageScale);
        };
        //降序
        Page<Posts> rs = postsDao.findAllDescSortReplyByTopic(topicId, filterMemeberId, pageable);
        Consumer<PostsReplica> pc = (PostsReplica pr)->{
            //解码图片连接编码
            String postsBody = ForumPostsDecorator.init(pr)
                    .block("*")
                    .decorateUploadImageUseScale(lazyLoad, imageScale)
                    .apply(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName())
                    .getContent();
            pr.setContent(postsBody);
        };
        return copyPosts(rs, pc);
    }*/
    /*
    @Override
    public Optional<PostsReplica> getPostsContentForEditByTopic(long topicId, ImageIOMeta imageIO)throws IllegalArgumentException,ReplicableException {
        if (topicId <= 0) {
            return Optional.empty();//("参数不合法或不被接受");
        }
        PostsReplica posts = getTopicContent(topicId).orElseThrow(()->new IllegalArgumentException("话题内容不存在"));
        String postsBody = ForumPostsDecorator.init(posts)
                            .decorateUploadImageEmptyScale(false)
                            .apply(imageIO.getImageBucketDomain(), imageIO.getUploadImageDirectName())
                            .getContent();
        posts.setContent(postsBody);
        return Optional.of(posts);
    }*/
    /*
    @Override
    public Optional<PostsReplica> getTopicContent(final long topicId){ //DEP03
        CompletableFuture<Optional<Posts>> r = CompletableFuture.supplyAsync(()->postsDao.findOneByTopicId(topicId)).completeOnTimeout(Optional.empty(), 1, TimeUnit.SECONDS);
        CompletableFuture<Topic> t = CompletableFuture.supplyAsync(()->topicDao.findOneById(topicId)).completeOnTimeout(null, 1, TimeUnit.SECONDS);
        return r.thenCombine(t, (opPosts, topic)->{
            Optional<PostsReplica> oppo = opPosts.map(PostsReplica.copyPosts);
            oppo.ifPresent(prObj->prObj.setTopic(topic));
            return oppo;
        }).orTimeout(1, TimeUnit.SECONDS).join();
    }
    
    @Override
    public Optional<PostsReplica> get(final long id, final long topicId){ //DEP01
        return getAsync(id, topicId).orTimeout(1, TimeUnit.SECONDS).join();
    }*/
    
    
    private Page<Posts> copyPosts(Page<Posts> rs, final Consumer<Posts> postsConsumer){
        List<Posts> posts = rs.getResult().collect(Collectors.toList());
        if (null == posts || posts.isEmpty()) {
            return Page.empty();
        }
        Stream<Posts> result = posts.parallelStream().peek(postsConsumer).filter(Objects::nonNull);
        return new Page<Posts>() {
            @Override
            public long getTotalElements() {
                return rs.getTotalElements();
            }
            
            @Override
            public Stream<Posts> getResult() {
                return result;
            }
        };
    }
/*
    private CompletableFuture<Optional<PostsReplica>> getAsync(final long id, final long topicId){
        CompletableFuture<Topic> tc = CompletableFuture.supplyAsync(()->topicDao.findOneById(topicId)).completeOnTimeout(null, 1, TimeUnit.SECONDS);
        return getAsync(id).thenCombine(tc, (posts, topic)->{
            Optional<PostsReplica> pr = Optional.ofNullable(posts).map(PostsReplica.copyPosts);
            pr.ifPresent(prObj->prObj.setTopic(topic));
            return pr;
        });
    }*/
}